package org.digitarts.rtms.helpers;

import static javax.xml.xpath.XPathConstants.NODE;
import static javax.xml.xpath.XPathConstants.NODESET;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class XMLHelper
{
	static
	{
		String xmlParser = System.getProperty("org.xml.sax.parser");
		if (xmlParser == null)
		{
			System.setProperty("org.xml.sax.parser", "com.sun.org.apache.xerces.internal.parsers.SAXParser");
		}
	}

	public static String getAttributeValueByName(NamedNodeMap nnm, String name)
	{
		for (int i = 0; i < nnm.getLength(); i++)
		{
			Attr attr = (Attr) nnm.item(i);
			if (attr.getName().equalsIgnoreCase(name))
			{
				return attr.getValue();
			}
		}
		return null;
	}

	public static int getAttributeIntValueByName(Node node, String name)
	{
		try
		{
			return Integer.parseInt(getAttributeValueByName(node.getAttributes(), name));
		}
		catch (Exception e)
		{
			throw new RuntimeException("Failed to get IntValue [" + name + "]", e);
		}
	}

	public static long getAttributeLongValueByName(Node node, String name)
	{
		try
		{
			return Long.parseLong(getAttributeValueByName(node.getAttributes(), name));
		}
		catch (Exception e)
		{
			throw new RuntimeException("Failed to get LongValue [" + name + "]", e);
		}
	}

	public static String getNodeTextValue(Node node)
	{
		return node.getFirstChild().getNodeValue();
	}

	public static String getAttributeValueByName(Node node, String name)
	{
		return getAttributeValueByName(node.getAttributes(), name);
	}

	public static boolean getAttributeBooleanByName(NamedNodeMap nnm, String name) throws RuntimeException
	{
		for (int i = 0; i < nnm.getLength(); i++)
		{
			Attr attr = (Attr) nnm.item(i);
			if (attr.getName().equalsIgnoreCase(name))
			{
				String tmp = attr.getValue().toLowerCase();
				if (tmp.equalsIgnoreCase("true"))
					return true;
				if (tmp.equalsIgnoreCase("false"))
					return false;
				throw new RuntimeException("Attribute " + name + " value not boolean:" + tmp);
			}
		}
		throw new RuntimeException("Attribute " + name + " not found.");
	}

	public static Node getChildNodeByName(Element element, String name, boolean caseSensitive)
	{
		NodeList list = element.getChildNodes();
		for (int i = 0; i < list.getLength(); i++)
		{
			Node node = list.item(i);
			if (caseSensitive)
			{
				if (node.getNodeName().equals(name))
					return node;
			}
			else
			{
				if (node.getNodeName().equalsIgnoreCase(name))
					return node;
			}
		}
		return null;
	}

	public static Node getChildNodeByName(Node element, String name, boolean caseSensitive)
	{
		NodeList list = element.getChildNodes();
		for (int i = 0; i < list.getLength(); i++)
		{
			Node node = list.item(i);
			if (caseSensitive)
			{
				if (node.getNodeName().equals(name))
					return node;
			}
			else
			{
				if (node.getNodeName().equalsIgnoreCase(name))
					return node;
			}
		}
		return null;
	}

	public static List<Node> getChildNodesByName(Node element, String name, boolean caseSensitive)
	{
		ArrayList<Node> nodes = new ArrayList<Node>();
		NodeList list = element.getChildNodes();
		for (int i = 0; i < list.getLength(); i++)
		{
			Node node = list.item(i);
			if (caseSensitive)
			{
				if (node.getNodeName().equals(name))
					nodes.add(node);
			}
			else
			{
				if (node.getNodeName().equalsIgnoreCase(name))
					nodes.add(node);
			}
		}
		return nodes;
	}

	public static Document parseXML(InputSource is)
	{
		try
		{
			Document doc = null;
			DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = documentBuilder.parse(is);
			return doc;
		}
		catch (Exception e)
		{
			throw new RuntimeException("Failed to parse XML source", e);
		}
	}

	public static Document parseXML(InputStream is)
	{
		return parseXML(new InputSource(is));
	}

	public static Document parseXML(File file)
	{
		FileInputStream fis = null;
		try
		{
			fis = new FileInputStream(file);
			return parseXML(fis);
		}
		catch (Exception e)
		{
			throw new RuntimeException("Failed to open XML file:" + file, e);
		}
		finally
		{
			try
			{
				fis.close();
			}
			catch (Exception e)
			{
			}
		}
	}

	public static Document parseXML(String xml)
	{
		StringReader sr = new StringReader(xml);
		return parseXML(new InputSource(sr));
	}

	public static List<Node> xGetNodes(Node targetNode, String expression)
	{
		List<Node> nodes = new ArrayList<Node>();
		XPath xpath = null;
		try
		{
			xpath = XPathFactory.newInstance().newXPath();
			XPathExpression xpathExpression = xpath.compile(expression);
			NodeList nodeList = (NodeList) xpathExpression.evaluate(targetNode, NODESET);
			if (nodeList != null)
			{
				for (int i = 0; i < nodeList.getLength(); i++)
				{
					nodes.add(nodeList.item(i));
				}
			}
			return nodes;
		}
		catch (Exception e)
		{
			throw new RuntimeException("XPath:Failed to locate the nodes:" + expression, e);
		}
	}

	public static Node xGetNode(Node targetNode, String expression)
	{
		Node node = null;
		XPath xpath = null;
		try
		{
			xpath = XPathFactory.newInstance().newXPath();
			XPathExpression xpathExpression = xpath.compile(expression);
			node = (Node) xpathExpression.evaluate(targetNode, NODE);
			return node;
		}
		catch (Exception e)
		{
			throw new RuntimeException("XPath:Failed to locate the node:" + expression, e);
		}
	}
}
